package main

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"go.uber.org/zap"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
	"laxkits/lib/laxproxy"
	"laxkits/lib/laxutils"
)

const (
	// 消息头的flag
	// ptc
	PTC_TYPE_FLAG = 0
	// rpc
	RPC_TYPE_FLAG           = 1
	RPC_TYPE_REPLY          = 1
	RPC_TYPE_ISREQUEST_MASK = 2
	RPC_TYPE_REQUEST        = 3
	// 压缩
	PTC_TYPE_COMPRESS   = 4
	RPC_TYPE_NULL_REPLY = 8
	PTC_TYPE_KEEP_ALIVE = 16
)

// 用户自定义数据
type RORoleContext struct {
	// 登录账号
	openId string
	// 角色id
	roleId uint64
	// 角色名字
	userName string
	// 要连接的服务器地址
	connectServerAddress string
}

// 序列化成字符串,日志输出用
func (c *RORoleContext) String() string {
	return fmt.Sprintf("%s,%d,%s", c.openId, c.roleId, c.userName)
}

func (c *RORoleContext) GetConnectServerAddress() (string, error) {
	if len(c.connectServerAddress) > 0 {
		return c.connectServerAddress, nil
	}
	return "", errors.New("Wait")
}

func (c *RORoleContext) GetRoleUid() uint64 {
	return c.roleId
}

func (c *RORoleContext) GetRoleName() string {
	return c.userName
}

// @Desc: RO的协议头信息
type ROProtoHead struct {
	laxproxy.DefaultHookHeader
	// 标记类型,二进制不同位不同意义,上面的const
	ProtoFlag uint32
	// tagid rpc专用
	TagId uint32
}

func (h *ROProtoHead) HasCompress() bool { return (h.ProtoFlag & PTC_TYPE_COMPRESS) > 0 }

// @Desc: 适用于RO的codec
type ROCodec struct {
	laxproxy.DefaultHookCodec
}

func (c *ROCodec) DecodeHead(buf []byte) (laxproxy.IHookHeader, error) {
	h := &ROProtoHead{}
	readBuf := bytes.NewReader(buf)
	if err := binary.Read(readBuf, binary.LittleEndian, &h.MsgBodyLen); err != nil {
		return nil, err
	}
	if err := binary.Read(readBuf, binary.LittleEndian, &h.ProtoId); err != nil {
		return nil, err
	}
	if err := binary.Read(readBuf, binary.LittleEndian, &h.ProtoFlag); err != nil {
		return nil, err
	}
	if (h.ProtoFlag&RPC_TYPE_FLAG) > 0 ||
		(h.ProtoFlag&RPC_TYPE_ISREQUEST_MASK) > 0 {
		if err := binary.Read(readBuf, binary.LittleEndian, &h.TagId); err != nil {
			return nil, err
		}
	}
	h.HeadLen = len(buf) - readBuf.Len()
	return h, nil
}

func (c *ROCodec) TryEncode(header laxproxy.IHookHeader, msgBody []byte) ([]byte, error) {
	head, ok := header.(*ROProtoHead)
	if !ok {
		return msgBody, nil
	}
	writeBuf := bytes.NewBuffer(nil)
	if err := binary.Write(writeBuf, binary.LittleEndian, uint32(len(msgBody)+head.HeadLen-4)); err != nil {
		return nil, err
	}
	if err := binary.Write(writeBuf, binary.LittleEndian, head.ProtoId); err != nil {
		return nil, err
	}
	if err := binary.Write(writeBuf, binary.LittleEndian, head.ProtoFlag); err != nil {
		return nil, err
	}
	if (head.ProtoFlag&RPC_TYPE_FLAG) > 0 ||
		(head.ProtoFlag&RPC_TYPE_ISREQUEST_MASK) > 0 {
		if err := binary.Write(writeBuf, binary.LittleEndian, head.TagId); err != nil {
			return nil, err
		}
	}
	if err := binary.Write(writeBuf, binary.LittleEndian, msgBody); err != nil {
		return nil, err
	}
	return writeBuf.Bytes(), nil
}

func (c *ROCodec) PrintMessage(userCtx laxproxy.IProxyUserContext, header laxproxy.IHookHeader, msgBody []byte, toServer bool) {
	if ctx, ok := userCtx.(*RORoleContext); ok {
		if head, ok := header.(*ROProtoHead); ok {
			from, to := "客户端", "服务器"
			if !toServer {
				from, to = to, from
			}
			msgType := "PTC"
			if (head.ProtoFlag & RPC_TYPE_FLAG) > 0 {
				msgType = "RPC"
			}
			if protoNode := GetProtoNode(head.ProtoId); protoNode != nil {
				isIgnore := false
				for _, v := range globalXmlNode.IgnoreProtoList {
					if v == protoNode.Name {
						isIgnore = true
						break
					}
				}
				if !isIgnore {
					zap.S().Infof("[协议] %s 从 <%s> 到 <%s> 的 <%s> 协议<%d> Name< %s\t%s > size<%s>",
						ctx.String(),
						from,
						to,
						msgType,
						head.ProtoId,
						protoNode.Name,
						protoNode.LowerName,
						laxutils.FormatNetBytes(int64(header.GetBodyLen())))
				}
				for _, v := range globalXmlNode.PrintProtoList {
					if v == protoNode.Name {
						dataName := protoNode.ArgName
						if (head.ProtoFlag & RPC_TYPE_FLAG) > 0 {
							if !toServer {
								dataName = protoNode.ResName
							}
						} else {
							dataName = protoNode.PtcDataName
						}
						if msgRefType, err := protoregistry.GlobalTypes.FindMessageByName(protoreflect.FullName("KKSG." + dataName)); err == nil {
							msgEntity := msgRefType.New().Interface()
							if err = proto.Unmarshal(msgBody, msgEntity); err == nil {
								if jsonValue, err := json.MarshalIndent(msgEntity, "", "    "); err == nil {
									zap.S().Infof("[数据] %s 从 <%s> 到 <%s> 的 <%s> 协议<%d> 参数< %s > 内容<\n%s >",
										ctx.String(),
										from,
										to,
										msgType,
										head.ProtoId,
										dataName,
										jsonValue)
								}
							}
						}
						break
					}
				}
				return
			}
			zap.S().Infof("[协议] %s 从 [%s] 到 [%s] 的 [%s] 协议[%d], 名字未知",
				ctx.String(),
				from,
				to,
				msgType,
				head.ProtoId)
		}
	}
}
func (c *ROCodec) TryBuildWholeMsg(protoId uint32, isRpc bool, msgBody []byte) []byte {
	h := ROProtoHead{}
	h.MsgBodyLen = uint32(len(msgBody))
	h.TagId = 0
	h.ProtoFlag = PTC_TYPE_FLAG
	h.HeadLen = 12
	h.ProtoId = protoId
	if isRpc {
		h.ProtoFlag = RPC_TYPE_REQUEST
		h.HeadLen = 16
	}
	result, err := c.TryEncode(&h, msgBody)
	if err != nil {
		zap.S().Infof("打包数据失败:%v", err)
		return nil
	}
	return result
}
